TechNote - Colors
February 19, 2023
Last updated on October 27, 2026
OneNote lets you change the view mode of pages between light mode and dark mode. When you switch this, the OneNote color palette changes how it renders colors. This article describes that transformation. This information can be used to replicate the color picker control and intelligently apply colors for light and dark mode snippets and other manipulations.
On the left is the color picker when the page background is set to a "light" color or light mode is set on the ribbon bar. But that same color picker changes to the one shown on the right when the page background a "dark" color or dark mode is set on the ribbon bar.
|
|
|
|
How OneNote translates colors between light and dark mode
OneNote stores one representation of every color: the light-mode value. In dark mode it draws that stored value through a translation. The translation is purely visual and never changes the XML. For example, stored ■ #FFC000 is drawn as ■ #664E00 on a dark page and as ■ #FFC000 on a light page.
The rendering rule has three steps, checked in this order:
- Palette lookup. The 70 picker colors (six theme rows plus the Standard Colors row) use a fixed table. Each light swatch is drawn as the dark swatch in the same position..
- Dark-theme grays. Stored ■ #EDEBE9 is drawn as ■ #000000 and stored ■ #201F1E as □ #FFFFFF.
- Everything else. HSL lightness is inverted. Hue and saturation are unchanged
.
When a user picks or enters a color in dark mode, OneNote stores the light-mode value that will make OneNote draw the color they chose. A MoreColorPicker should do the same: show the mode-appropriate palette, and always store the light-mode value.
Why a lookup and a formula
The formula fits every off-palette color that was measured (22 of 22, exact to the last hex digit) but reproduces only 7 of the 69 distinct palette colors. Standard-row gold ■ #FFC001 renders as ■ #893A06, while theme gold ■ #FFC000, one blue step away, renders as ■ #664E00. A continuous formula can't do that. It was then confirmed directly that stored ■ #FFC000 draws as ■ #664E00, while the formula alone would have drawn it unchanged. So the table is real: OneNote draws palette colors from a table and everything else from a formula.
Black and white are not exceptions. ■ #000000 → ■ #EDEBE9 and □ #FFFFFF → ■ #201F1E are the first two entries in the palette table.
Storage versus display
- The XML holds light-mode colors, even when the page is in dark mode.
- Switching modes never rewrites the XML.
- If the user switches back to light mode, stored colors draw as the light palette colors. This is why the stored value for a dark swatch must be the matching light swatch, not whatever value would draw the same color.
- Automatic text color has no stored RGB value. It is drawn as ■ #EDEBE9 on a dark page.
Picking or entering a color in dark mode
|
User action |
Store |
|
Clicks a dark swatch |
The light swatch in the same position (dark gold ■ #664E00 stores ■ #FFC000) |
|
Enters ■ #FFFFFF |
■ #201F1E (matches OneNote) |
|
Enters ■ #000000 |
■ #EDEBE9 |
|
Enters a color that equals any other dark swatch |
That swatch's light counterpart |
|
Enters any other color |
The formula's inverse of the entered color |
Storing a formula value for a swatch would draw the right color on a dark page but the wrong color after the user switches to light mode (■ #FFE799 instead of gold ■ #FFC000).
Two stored values draw as white on a dark page: ■ #201F1E and ■ #171616. Clicking the dark white swatch (row 6, column 3) stores ■ #171616, while typing □ #FFFFFF stores ■ #201F1E. Both look the same in dark mode.
The formula
New HSL lightness = 100% − L, with H and S unchanged. In RGB terms, add one offset to all three channels:
offset = 255 − max(R,G,B) − min(R,G,B)
new_ch = old_ch + offset
For example, ■ #66CC66 has max 204 and min 102, so the offset is −51 and the result is ■ #339933.
- Grays become exact RGB inversions (■ #202020 → ■ #DFDFDF).
- Colors with max + min = 255 don't change (■ #FF0000, ■ #0000FF, ■ #00FF00, ■ #FF8000).
- Results stay within 0–255, so no clamping is needed.
- Near-black and near-white are ordinary inputs: ■ #010101 → ■ #FEFEFE, ■ #FEFEFE → ■ #010101.
- The formula is its own inverse. The full translation is not, because of the table.
C# routine
static class OneNoteDarkMode
{
// Palette, position for position: light swatch -> dark swatch.
static readonly string[] Light =
{
"FFFFFF 000000 E7E6E6 44546A 5B9BD5 ED7D31 E84C22 FFC000 8064A2 70AD47",
"F2F2F2 7F7F7F D0CECE D6DCE4 DEEBF6 FBE5D5 FADBD2 FFF2CC E5E0EC E2EFD9",
"D8D8D8 595959 AEABAB ADB9CA BDD7EE F7CBAC F5B7A6 FEE599 CCC1D9 C5E0B3",
"BFBFBF 3F3F3F 757070 8496B0 9CC3E5 F4B183 F1937A FFD965 B2A1C7 A8D08D",
"A5A5A5 262626 3A3838 323F4F 2E75B5 C55A11 B43512 BF9000 5F497A 538135",
"7F7F7F 0C0C0C 171616 222A35 1E4E79 833C0B 78230C 7F6000 3F3151 375623",
"C00000 FA0000 FFC001 FFFF00 92D050 00B050 00B0F0 0070C0 002060 7030A0"
};
static readonly string[] Dark =
{
"201F1E EDEBE9 1C1C1C BBC7D3 327DC3 C15710 EA5F39 664E00 AB99C2 517F34",
"0D0D0D 8F8F8F 544F4F 3C465D 1A4665 69310C 7F2A10 4D3B00 4E3C62 27441D",
"4D4D4D B3B0AD 6C6565 545783 1D5072 7B3909 9A3013 564201 614F7D 33521E",
"414140 E8E8E8 7C7676 677D9E 275F90 90440E BA3712 5C4500 7C5D98 405E26",
"5A5A5A D9D9D9 F2F2F2 E6EBEF 69A4D8 EC7627 F39A81 997300 CFC3DA 6CAA46",
"8F8F8F F3F3F3 FFFFFF F7F9FB B2D0EB F9C59F FCDED9 DBA400 F6F2F8 B6D9A1",
"FF6363 FF0505 893A06 8A6700 466D1D 00853C 018687 3DAEFF 76CAFF A76ED4"
};
static readonly Dictionary<int, int> toDark = new Dictionary<int, int>();
static readonly Dictionary<int, int> toLight = new Dictionary<int, int>();
static OneNoteDarkMode()
{
for (int r = 0; r < Light.Length; r++)
{
var l = Light[r].Split(' ');
var d = Dark[r].Split(' ');
for (int i = 0; i < l.Length; i++)
{
int lk = Convert.ToInt32(l[i], 16);
int dk = Convert.ToInt32(d[i], 16);
toDark[lk] = dk;
toLight[dk] = lk;
}
}
}
/// <summary>Stored (light-mode) color -> the color OneNote draws on a dark page.</summary>
public static Color ToDark(Color stored)
{
int key = stored.ToArgb() & 0xFFFFFF;
if (toDark.TryGetValue(key, out int dark)) return Rgb(stored.A, dark);
if (key == 0xEDEBE9) return Color.FromArgb(stored.A, 0x00, 0x00, 0x00);
if (key == 0x201F1E) return Color.FromArgb(stored.A, 0xFF, 0xFF, 0xFF);
return InvertLightness(stored);
}
/// <summary>
/// A color the user entered in dark mode -> the light-mode value to store in the XML.
/// For a swatch click, use the swatch's own light counterpart directly.
/// </summary>
public static Color ToLight(Color entered)
{
int key = entered.ToArgb() & 0xFFFFFF;
// before the table: swatch #FFFFFF would give #171616
if (key == 0xFFFFFF) return Color.FromArgb(entered.A, 0x20, 0x1F, 0x1E);
if (key == 0x000000) return Color.FromArgb(entered.A, 0xED, 0xEB, 0xE9);
if (toLight.TryGetValue(key, out int light)) return Rgb(entered.A, light);
return InvertLightness(entered);
}
static Color Rgb(int a, int rgb) =>
Color.FromArgb(a, (rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF);
static Color InvertLightness(Color c)
{
int offset = 255 - Math.Max(c.R, Math.Max(c.G, c.B))
- Math.Min(c.R, Math.Min(c.G, c.B));
return Color.FromArgb(c.A, c.R + offset, c.G + offset, c.B + offset);
}
}
Guidance for OneMore
- Reading colors from XML. The value is the light-mode color. For what the user sees on a dark page, call ToDark.
- Calculations based on appearance. Contrast checks, readable text over a cell background, and dialog swatches meant to match the page should use ToDark when the page is dark.
- Writing colors chosen in dark mode. Call ToLight for entered values, and use each swatch's light counterpart for swatch clicks. Never store the dark value itself.
- XML-to-XML work. Copying, merging or recoloring existing content needs no conversion.
- Automatic color. Where OneMore needs a concrete color, use ■ #EDEBE9 on a dark page. For a light page use ■ #000000, which is inferred and wasn't measured.
Evidence
Stored ■ #FFC000 draws as ■ #664E00 on a dark page, and clicking the ■ #664E00 swatch stores ■ #FFC000 (observed).
22 off-palette colors measured, all matching the formula
|
Stored |
Displayed |
|
Stored |
Displayed |
|
■ #777777 |
■ #888888 |
|
■ #0000FF |
■ #0000FF |
|
■ #808080 |
■ #7F7F7F |
|
■ #8000FF |
■ #8000FF |
|
■ #999999 |
■ #666666 |
|
■ #FF00FF |
■ #FF00FF |
|
■ #202020 |
■ #DFDFDF |
|
■ #00FF00 |
■ #00FF00 |
|
■ #505050 |
■ #AFAFAF |
|
■ #00FFFF |
■ #00FFFF |
|
■ #B0B0B0 |
■ #4F4F4F |
|
■ #FF8000 |
■ #FF8000 |
|
■ #E0E0E0 |
■ #1F1F1F |
|
■ #00C000 |
■ #3FFF3F |
|
■ #FF0000 |
■ #FF0000 |
|
■ #66CC66 |
■ #339933 |
|
■ #E00000 |
■ #FF1F1F |
|
■ #6666CC |
■ #333399 |
|
■ #A00000 |
■ #FF5F5F |
|
■ #CC6666 |
■ #993333 |
|
■ #800000 |
■ #FF7F7F |
|
■ #998877 |
■ #887766 |
Black, white and near-black / near-white (measured)
|
Applied |
Displayed |
|
■ #000000 |
■ #EDEBE9 |
|
□ #FFFFFF |
■ #201F1E |
|
■ #EDEBE9 |
■ #000000 |
|
■ #201F1E |
□ #FFFFFF |
|
■ #010101 |
■ #FEFEFE |
|
■ #FEFEFE |
■ #010101 |
|
Automatic |
■ #EDEBE9 |
Dark-mode white entered by the user is stored as ■ #201F1E.
The palette table is the two TechNote palette screenshots, which are the source of truth. It has no conflicts: no light swatch maps to two dark swatches and no dark swatch comes from two light swatches.
Two earlier models were wrong. A perceptual-lightness reflection (CIE L*) approximated the palette, and a claim that the dark picker was unrelated to the renderer was also wrong. The lookup explains both sets of data.
Open items
- Exact or approximate matching. Apply ■ #FFC001 and ■ #FFC002 on a dark page. ■ #FFC001 is a different palette entry and should draw as ■ #893A06. ■ #FFC002 isn't in the palette, and the formula predicts it draws unchanged. If it draws near ■ #664E00, OneNote matches nearby colors.
- A typed color that matches no swatch. Enter ■ #A54DCA in dark mode in OneNote's own dialog. The formula predicts it stores ■ #8D35B2.
- A typed color that equals a dark swatch other than white. Enter ■ #664E00 and check the XML. The routine stores ■ #FFC000, but OneNote hasn't been observed doing it. The □ #FFFFFF case shows that typed entries can differ from swatch clicks, so this deserves a check. ■ #F2F2F2 is another good one, since it's a dark swatch (← ■ #3A3838) and the formula would give ■ #0D0D0D.
- Other elements. Text, highlights, table cell shading and the page background are all assumed to follow the same rules. Only what was tested is confirmed.
- Automatic text. The light-mode value ■ #000000 is inferred.
- Measurement noise. All measurements were eyedropped from rendered screens. If a value is off by 1–2 in a channel, check display color management and anti-aliasing before doubting the rule.
Registry for Office Themes
OneNote follows the Microsoft Office theme. All Office apps are controlled by a single Registry key
Computer\HKEY_CURRENT_USER\Software\Microsoft\Office\16.0\Common
The key has one of two properties based on Office version (I believe). They are Theme or UI Theme. Valid values for this property are:
|
Colorful |
0 |
|
Dark Gray |
3 |
|
Black |
4 |
|
White |
5 |
#omwiki #omdeveloper #omtechnote
© 2020 Steven M Cohn. All rights reserved.
Please consider a sponsorship or one-time donation to support ongoing development
Created with OneNote.

